package info.u250.c2d.engine;
import info.u250.c2d.accessors.C2dCameraAccessor;
import info.u250.c2d.accessors.FloatValueAccessor;
import info.u250.c2d.accessors.FloatValueAccessor.FloatValue;
import info.u250.c2d.accessors.MeshMaskAccessor;
import info.u250.c2d.accessors.SpriteAccessor;
import info.u250.c2d.engine.CoreProvider.CoreEvents;
import info.u250.c2d.engine.CoreProvider.TransitionType;
import info.u250.c2d.engine.EngineDrive.EngineOptions;
import info.u250.c2d.engine.events.EventManager;
import info.u250.c2d.engine.events.EventManagerImpl;
import info.u250.c2d.engine.load.Loading.LoadingComplete;
import info.u250.c2d.engine.load.in.InGameLoading;
import info.u250.c2d.engine.load.startup.SimpleLoading;
import info.u250.c2d.engine.load.startup.StartupLoading;
import info.u250.c2d.engine.resources.AliasResourceManager;
import info.u250.c2d.engine.resources.LanguagesManager;
import info.u250.c2d.engine.resources.MusicManager;
import info.u250.c2d.engine.resources.SoundManager;
import info.u250.c2d.engine.transitions.TransitionFactory;
import info.u250.c2d.graphic.FadeMask;
import info.u250.c2d.graphic.surfaces.TriangleSurfaces;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenManager;
import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Preferences;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.maps.tiled.TiledMap;
import com.badlogic.gdx.maps.tiled.TmxMapLoader;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
/**
* The main game engine . it supply many usefully static method to access the managers of the game.
* you game should begin here , and simply extends this.
* @author lycying@gmail.com
*/
public abstract class Engine extends ApplicationAdapter{
private static Engine instance = null;
public Engine(){
instance = this;
this.engineDrive = this.onSetupEngineDrive();
this.engineConfig = engineDrive.onSetupEngine();
}
@SuppressWarnings("unchecked")
public static final <T extends Engine> T get(){
return (T)instance;
}
private EngineCallback engineCallback = new DefaultEngineCallback();
public static void setEngineCallback(EngineCallback engineCallback){
instance.engineCallback = engineCallback;
}
public static EngineCallback getEngineCallback(){
return instance.engineCallback;
}
private ShapeRenderer shapeRenderer;
/** the game logic */
private EngineDrive engineDrive;
/** the game configure */
private EngineOptions engineConfig;
/** the event manager */
private EventManager eventManager;
/** the core asset manager of Libgdx */
private AssetManager assetManager ;
/** default bitmap font */
private BitmapFont defaultFont;
/** default sprite batch */
private SpriteBatch spriteBatch ;
/** default camera */
private C2dCamera defaultCamera;
/** the loadding screen */
private StartupLoading startupLoading ;
/** the in game loading */
private InGameLoading ingameLoading;
/** the music manager */
private MusicManager musicManager;
/** the sound manager */
private SoundManager soundManager;
/** the resources alias manager */
private AliasResourceManager<String> aliasResourceManager;
/** LanguagesManager */
private LanguagesManager languagesManager;
/** Preferences */
private Preferences preferences;
/** the fps label */
private C2dFps fps;
/** the main game scene to show the graphic */
private Scene mainScene;
/** the transition scene to switch two scene */
private Transition transitionScene;
/** its a event manager that [Aurelien Ribon] supply */
private TweenManager tweenManager;
/** if the game is running */
private boolean running = true;
/**
* This is one of the most important methods.
* The typical, all the game logic and rendering entry will go from here.
* We do this purpose, main is to let business operation independent,
* and you may no longer care about many things such as resources destroyed, load control etc,
* and to focus on the game itself.
*/
protected abstract EngineDrive onSetupEngineDrive();
/**
* Set the main of operation, played the role of the page. You should never use this method
*/
protected final static void _setMainScene(Scene mainScene){
instance.mainScene = mainScene;
}
/**
* When in the scene when switching between.
* Will cause a series of gradual movement.
* The last. New rendering methods will replace the old
*/
public final static void setMainScene(Scene mainScene){
setMainScene(mainScene, TransitionType.Fade);
}
public final static void setMainScene(Scene mainScene,TransitionType type){
Engine.setMainScene(mainScene, type, 500);
}
public final static void setMainScene(Scene mainScene,TransitionType type,int halfDurationMillis){
Engine.doResume();
if(instance.transitionScene.isTransiting()) return;
if(instance.mainScene == mainScene ) return ;
instance.transitionScene = TransitionFactory.getTransitionScene(type);
Gdx.input.setInputProcessor(null);
instance.transitionScene.transition(instance.mainScene, mainScene,halfDurationMillis);
}
/**
* You may want to know what's the main scene . Call this
*/
public final static Scene getMainScene(){
return Engine.instance.mainScene;
}
/**
* First of all, load resources.
* When resource loading was finished,
* we will enter the game initialization operation.
*/
@Override
public final void create () {
try{
this.shapeRenderer = new ShapeRenderer();
//set up the FPS
if(engineConfig.debug)this.fps = new C2dFps();
if(engineConfig.catchBackKey)Gdx.input.setCatchBackKey(true);
//set up the TweenEngine
this.setupTweenEngine();
//set up the camera
this.setupCamera();
//the resource manager
this.assetManager = new AssetManager();
this.assetManager.setLoader(TiledMap.class, new TmxMapLoader(new InternalFileHandleResolver()));
this.aliasResourceManager = new AliasResourceManager<String>();
this.soundManager = new SoundManager();
this.musicManager = new MusicManager();
this.languagesManager = new LanguagesManager();
//the event manager
this.eventManager = new EventManagerImpl();
//set up the sprite batch
this.spriteBatch = new SpriteBatch();
//set up the default font
if(Gdx.app.getType()==ApplicationType.WebGL || Gdx.app.getType() == ApplicationType.iOS){
this.defaultFont = new BitmapFont(Gdx.files.internal("data/font-c2d.fnt"),false);
}else{
this.defaultFont = new BitmapFont();
}
//set up the default preferences
this.preferences = Gdx.app.getPreferences(engineConfig.configFile);
if(null!=engineCallback){
engineCallback.preLoad(Gdx.graphics.getDisplayMode(),engineConfig.assets);
}
//loading screen
this.setupLoading();
}catch(Exception ex){
ex.printStackTrace();
}
}
protected StartupLoading getStartupLoading(){
return new SimpleLoading();
}
protected InGameLoading getInGameLoading(){
return new info.u250.c2d.engine.load.in.SimpleLoading();
}
private void setupLoading() {
this.startupLoading = getStartupLoading();
this.ingameLoading = getInGameLoading();
this.startupLoading.setLoaded(false);
this.ingameLoading.setLoaded(true);
this.startupLoading.setLoadingComplete(new LoadingComplete() {
@Override
public void onReady(AliasResourceManager<String> alias) {
transitionScene = TransitionFactory.getTransitionScene(TransitionType.Fade);
engineDrive.onResourcesRegister(aliasResourceManager);
engineDrive.onLoadedResourcesCompleted();
if(null!=engineCallback){
engineCallback.postLoad();
}
}
});
}
private void setupTweenEngine(){
this.tweenManager = new TweenManager();
Tween.registerAccessor(Sprite.class,new SpriteAccessor());
Tween.registerAccessor(C2dCamera.class, new C2dCameraAccessor());
Tween.registerAccessor(FadeMask.class, new MeshMaskAccessor());
Tween.registerAccessor(FloatValue.class, new FloatValueAccessor());
}
private void setupCamera(){
this.defaultCamera = new C2dCamera(this.engineConfig.width,this.engineConfig.height);
}
@Override
public final void pause() {
doPause();
eventManager.fire(CoreEvents.SystemPause, this);
super.pause();
}
@Override
public final void resume() {
if(engineConfig.autoResume)
doResume();
eventManager.fire(CoreEvents.SystemResume, this);
super.resume();
}
public final static void doPause() {
instance.running = false;
instance.tweenManager.pause();
}
public final static void doResume() {
instance.running = true;
instance.tweenManager.resume();
}
public final static boolean isPause(){
return !instance.running;
}
@Override
public final void render() {
if(null == startupLoading)return;
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
final float delta = Engine.getDeltaTime();
this.defaultCamera.update();
this.spriteBatch.setProjectionMatrix(this.defaultCamera.combined);
if(startupLoading.isLoaded()){
if(running){
if(null == mainScene){
Gdx.app.log("Error", "you must supply a main Scene , Set it at EngineDriveer#onLoadedResourcesCompleted()");
System.exit(-1);
}
tweenManager.update(delta*1000);
mainScene.update(delta);
}
eventManager.update(delta);
if(transitionScene.isTransiting()){
transitionScene.render(delta);
}else{
mainScene.render(delta);
}
//the in game loading
if(!ingameLoading.isLoaded()){
ingameLoading.render(delta);
}
if(engineConfig.debug)fps.render(delta);
}else{
startupLoading.render(delta);
}
}
/**
* In game load.
*/
public final static void load(String[] assets,LoadingComplete loadingComplete){
if(null == assets || 0 == assets.length) return ;
instance.ingameLoading.setLoadingComplete(loadingComplete);
instance.ingameLoading.setLoaded(false);
for(final String path:assets){
instance.aliasResourceManager.load(path);
}
}
public final static <T> T resource(String key){
return instance.aliasResourceManager.get(key);
}
public final static <T> T resource(String key,Class<T> type){
return instance.aliasResourceManager.get(key);
}
//Change to OrthographicCamera
// private static Ray ray = null;
// private final static Plane xzPlane = new Plane(new Vector3(0, 1, 0), new Vector3(1, 0, 0),new Vector3(1, 1, 0));
// private final static Vector3 intersection = new Vector3();
private final static Vector3 unproject = new Vector3();
public final static Vector2 screenToWorld(float x, float y) {
// ray = instance.defaultCamera.getPickRay(x, y);
// Intersector.intersectRayPlane(ray, xzPlane, intersection);
instance.defaultCamera.unproject(unproject.set(x, y, 0));
// screenCoords.x = Vector3.tmp.x;
// screenCoords.y = Vector3.tmp.y;
return new Vector2(unproject.x,unproject.y);
}
public final static void debugInfo(String str){
getSpriteBatch().begin();
getDefaultFont().draw(getSpriteBatch(), str, 0, getHeight());
getSpriteBatch().end();
}
public final static float getDeltaTime(){
return Math.min(1f/60f,Gdx.graphics.getDeltaTime());
}
public final static MusicManager getMusicManager() {
return instance.musicManager;
}
public final static AliasResourceManager<String> getAliasResourceManager(){
return instance.aliasResourceManager;
}
public final static SoundManager getSoundManager() {
return instance.soundManager;
}
/** you should never use this method , its only needed in Engine */
public final static AssetManager getAssetManager() {
return instance.assetManager;
}
public final static SpriteBatch getSpriteBatch() {
return instance.spriteBatch;
}
public final static C2dCamera getDefaultCamera() {
return instance.defaultCamera;
}
public final static float getHeight(){
return instance.engineConfig.height;
}
public final static boolean useGL20(){
return instance.engineConfig.useGL20;
}
public final static float getWidth(){
return instance.engineConfig.width;
}
public final static BitmapFont getDefaultFont(){
return instance.defaultFont;
}
public final static EventManager getEventManager(){
return instance.eventManager;
}
public final static TweenManager getTweenManager(){
return instance.tweenManager;
}
public final static LanguagesManager getLanguagesManager(){
return instance.languagesManager;
}
public final static Preferences getPreferences(){
return instance.preferences;
}
public final static ShapeRenderer getShapeRenderer(){
return instance.shapeRenderer;
}
@Override
public void resize(int width, int height) {
if(this.engineConfig.resizeSync){
this.engineConfig.width = width;
this.engineConfig.height = height;
this.defaultCamera.resize(width, height);
}
}
@Override
public void dispose() {
try{
TriangleSurfaces.disposeShader();//dispose the shader
this.tweenManager.killAll();
engineDrive.dispose();
if(null!=shapeRenderer){
shapeRenderer.dispose();
shapeRenderer = null;
}
if(null!=defaultFont){
defaultFont.dispose();
defaultFont = null;
}
if(null!=soundManager){
soundManager.dispose();
soundManager = null;
}
if(null!=musicManager){
musicManager.dispose();
musicManager = null;
}
if(startupLoading!=null){
if(!startupLoading.finished()){
this.assetManager.finishLoading();
}
startupLoading.dispose();
startupLoading = null;
}
if(null!=spriteBatch){
this.spriteBatch.dispose();
spriteBatch = null;
}
if(null!=preferences){
preferences.flush();
}
this.assetManager = null;
this.defaultCamera = null;
instance = null;
super.dispose();
}catch (Exception ex){
//ignore
ex.printStackTrace();
}
}
}